﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using SimpleIR;

namespace InterpreterCore
{
	/// <summary>
	/// This class represents the home locations for local variables and static fields
	/// </summary>
	internal sealed class CHomeLocation : CLocation
	{
		/// <summary>
		/// Value if the type is a signed number.
		/// </summary>
		private NumericValue m_numval;

		/// <summary>
		/// Value if the type is a pointer.
		/// </summary>
		private PtrValue m_ptrval;

		/// <summary>
		/// Value if the type is a pointer.
		/// </summary>
		private ReferenceValue m_refval;

		/// <summary>
		/// Value if the type is a struct.
		/// </summary>
		private CompositeValueEntry[] m_structval;

		private CHomeLocation(int address, IRType loctype)
			: base(address, loctype)
		{
			if(loctype is IREnumType)
				this.m_numval = NumericValue.MakeZeroValueOfType(((IREnumType)loctype).UnerlyingNumericType);
			else if(loctype is IRPrimitiveNumericType)
				this.m_numval = NumericValue.MakeZeroValueOfType((IRPrimitiveNumericType)loctype);
			else
				this.m_numval = NumericValue.MakeZeroValueOfType(null);

			if(loctype is IRPtrType)
				this.m_ptrval = PtrValue.MakeNullPtr((IRPtrType)loctype);
			else
				this.m_ptrval = PtrValue.MakeNullPtr(null);

			if(loctype is IRReferenceType)
				this.m_refval = ReferenceValue.MakeNullReference((IRReferenceType)loctype);
			else
				this.m_refval = ReferenceValue.MakeNullReference(null);

			if(loctype is IRStructType)
				this.m_structval = CompositeValue.MakeCompositeValue((IRStructType)loctype);
			else
				this.m_structval = null;
		}

		internal static CHomeLocation InterpAllocateHomeLocation(int address, IRType loctype)
		{ return new CHomeLocation(address, loctype); }

		public override string ToString()
		{
			if(this.LocationType is IRPrimitiveNumericType || this.LocationType is IREnumType)
				return this.m_numval.ToString();
			else if(this.LocationType is IRPtrType)
				return this.m_ptrval.ToString();
			else if(this.LocationType is IRReferenceType)
				return this.m_refval.ToString();
			else
				return CompositeValue.Stringify((CompositeValueEntry[])this.m_structval);
		}

		internal override string DebuggerString()
		{
			string dbgvals;

			if(this.LocationType is IRPrimitiveNumericType || this.LocationType is IREnumType)
				dbgvals = this.m_numval.DebuggerString();
			else if(this.LocationType is IRPtrType)
				dbgvals = this.m_ptrval.DebuggerString();
			else if(this.LocationType is IRReferenceType)
				dbgvals = this.m_refval.DebuggerString();
			else
				dbgvals = CompositeValue.DebuggerString((CompositeValueEntry[])this.m_structval);

			return String.Format("{0}:{1}", this.LocationType.BaseTypeName, dbgvals); 
		}

		internal override void SerializeForHeapDump(System.IO.TextWriter dumpstr)
		{
			string ohs = String.Format("LFlag=Home LAddr={0} LType={1} LValue=", this.Address, this.LocationType.TypeID);
			dumpstr.Write(ohs);

			if(this.LocationType is IRPrimitiveNumericType || this.LocationType is IREnumType)
				this.m_numval.SerializeForHeapDump(dumpstr);
			else if(this.LocationType is IRPtrType)
				this.m_ptrval.SerializeForHeapDump(dumpstr);
			else if(this.LocationType is IRReferenceType)
				this.m_refval.SerializeForHeapDump(dumpstr);
			else
				CompositeValue.SerializeForHeapDump(dumpstr, (CompositeValueEntry[])this.m_structval);
		}

		internal override void SerializeForDGMLDisplay(System.Xml.XmlWriter nodexw, System.Xml.XmlWriter edgexw, string lname)
		{
			if(this.LocationType is IRPtrType || this.LocationType is IRReferenceType)
			{
				nodexw.WriteStartElement("Node");
				nodexw.WriteAttributeString("Id", lname);
				nodexw.WriteAttributeString("Label", lname);
				nodexw.WriteAttributeString("Background", "gold");
				nodexw.WriteEndElement();

				int trgt;
				if(this.LocationType is IRReferenceType)
					trgt = this.m_refval.IsNullReference ? -1 : this.m_refval.TargetAddress;
				else
					trgt = this.m_ptrval.IsNullPtr ? -1 : this.m_ptrval.TargetAddress;

				if(trgt != -1)
				{
					edgexw.WriteStartElement("Link");
					edgexw.WriteAttributeString("Source", lname);
					edgexw.WriteAttributeString("Target", trgt.ToString());
					edgexw.WriteEndElement();
				}
			}
			else if(this.LocationType is IRStructType)
			{
				if(((IRStructType)this.LocationType).TypeDescription.AllTerminalAccessPaths.Any((IRBasicAccessPath bap) => bap.PathLast.FieldType is IRPtrType))
				{
					nodexw.WriteStartElement("Node");
					nodexw.WriteAttributeString("Id", lname);
					nodexw.WriteAttributeString("Label", lname);
					nodexw.WriteAttributeString("Background", "gold");
					nodexw.WriteEndElement();

					CompositeValueEntry[] cve = (CompositeValueEntry[])this.m_structval;
					for(int i = 0; i < cve.Length; ++i)
					{
						if(cve[i].Offset.PathLast.FieldType is IRPtrType)
						{
							PtrValue pv = cve[i].Value.ConvertToPassablePtrValue();
							if(!pv.IsNullPtr)
							{
								edgexw.WriteStartElement("Link");
								edgexw.WriteAttributeString("Source", lname);
								edgexw.WriteAttributeString("Target", pv.TargetAddress.ToString());
								edgexw.WriteAttributeString("Label", cve[i].Offset.FieldOnlyDisplayString());
								edgexw.WriteEndElement();
							}
						}
					}
				}
			}
			else
			{
				;
			}
		}

		internal static CHomeLocation HomeParseLocation(Dictionary<string, string> pkvp)
		{
			Debug.Assert(pkvp["LFlag"].Equals("Home"));

			int addr = Int32.Parse(pkvp["LAddr"]);

			int typeid = Int32.Parse(pkvp["LType"]);
			IRType ltype = GlobalInterpreterState.TProg.TypeTable.LookupTypeForID(typeid);

			CHomeLocation hl = new CHomeLocation(addr, ltype);
			string vstr = pkvp["LValue"];

			if(ltype is IRPrimitiveNumericType || ltype is IREnumType)
				hl.m_numval = NumericValue.ParseValue(ltype is IREnumType ? ((IREnumType)ltype).UnerlyingNumericType : (IRPrimitiveIntegerType)ltype, vstr);
			else if(ltype is IRPtrType)
				hl.m_ptrval = PtrValue.ParseValue((IRPtrType)ltype, vstr);
			else if(ltype is IRReferenceType)
				hl.m_refval = ReferenceValue.ParseValue((IRReferenceType)ltype, vstr);
			else
				hl.m_structval = CompositeValue.ParseValue(ltype, vstr);

			return hl;
		}

		//////////////////////////////////////////////////////
		//Support for basic load and store to/from home locations

		internal ReferenceValue ReadPassableReferenceValueFromHomeLocation()
		{
			TracingSystem.LogUseFromLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			return this.m_refval;
		}

		internal void StorePassableReferenceValToHomeLocation(ReferenceValue rv)
		{
			Debug.Assert(this.LocationType is IRReferenceType && this.LocationType == rv.ValType);

			TracingSystem.LogOldReference(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_refval);
			TracingSystem.LogModToLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			this.m_refval = rv;
		}

		internal PtrValue ReadPassablePtrValueFromHomeLocation()
		{
			TracingSystem.LogUseFromLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			return this.m_ptrval;
		}

		internal void StorePassablePtrValToHomeLocation(PtrValue pv)
		{
			Debug.Assert(this.LocationType is IRPtrType && this.LocationType == pv.ValType);

			TracingSystem.LogOldPointer(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_ptrval);
			TracingSystem.LogModToLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			this.m_ptrval = pv;
		}

		internal NumericValue ReadPassableNumericFromFromHomeLocation()
		{
			TracingSystem.LogUseFromLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			return this.m_numval;
		}

		internal void StorePassableNumericValToHomeLocation(NumericValue nv)
		{
			Debug.Assert(this.LocationType is IRPrimitiveNumericType || this.LocationType is IREnumType);
			Debug.Assert((this.LocationType is IRPrimitiveNumericType && this.LocationType == nv.ValType) || (this.LocationType is IREnumType && ((IREnumType)this.LocationType).UnerlyingNumericType == nv.ValType));

			TracingSystem.LogOldNumeric(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_numval);
			TracingSystem.LogModToLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			this.m_numval = nv;
		}

		internal CompositeValueEntry[] ReadStructFromHomeLocation()
		{
			Debug.Assert(this.LocationType is IRStructType);

			TracingSystem.LogUseFromLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			return CompositeValue.ReadCompositeFrom(IRBasicAccessPath.EmptyAccessPath, this.m_structval, this.LocationType);
		}

		internal void StoreCompositeToHomeLocation(CompositeValueEntry[] cv)
		{
			Debug.Assert(this.LocationType is IRStructType);

			TracingSystem.LogOldComposite(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_structval);
			TracingSystem.LogModToLocation(this.Address, IRBasicAccessPath.EmptyAccessPath, this.LocationType);

			CompositeValue.StoreCompositeTo(cv, IRBasicAccessPath.EmptyAccessPath, this.m_structval, this.LocationType);
		}

		//////////////////////////////////////////////////////
		//Overridden access methods.

		internal override IEnumerable<int> EnumerateTrgtAddresses()
		{
			if(this.LocationType is IRPtrType)
				yield return this.m_ptrval.TargetAddress;

			if(this.LocationType is IRReferenceType)
				yield return this.m_refval.TargetAddress;
			
			if(this.LocationType is IRStructType)
			{
				for(int j = 0; j < this.m_structval.Length; ++j)
				{
					if(this.m_structval[j].Offset.PathLast.FieldType is IRPtrType)
					{
						PtrValue cpv = this.m_structval[j].Value.ConvertToPassablePtrValue();
						yield return cpv.TargetAddress;
					}
				}
			}
		}

		internal override PtrValue ReadPassablePtrValueFrom(ReferenceValue rf)
		{
			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			if(rf.TargetOffset.IsEmptyAccessPath)
				return this.m_ptrval;
			else
				return CompositeValue.ReadPassablePtrValueFrom(rf.TargetOffset, this.m_structval);
		}

		internal override void StorePassablePtrValTo(PtrValue pv, ReferenceValue rf)
		{
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			if(rf.TargetOffset.IsEmptyAccessPath)
			{
				Debug.Assert(this.LocationType is IRPtrType && this.LocationType == pv.ValType);

				TracingSystem.LogOldPointer(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_ptrval);

				this.m_ptrval = pv;
			}
			else
			{
				TracingSystem.LogOldPointer(this.Address, rf.TargetOffset, CompositeValue.ReadPassablePtrValueFrom(rf.TargetOffset, this.m_structval));

				CompositeValue.StorePassablePtrValTo(pv, rf.TargetOffset, this.m_structval);
			}
		}

		internal override NumericValue ReadPassableNumericFrom(ReferenceValue rf)
		{
			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			if(rf.TargetOffset.IsEmptyAccessPath)
				return this.m_numval;
			else
				return CompositeValue.ReadPassableNumericFrom(rf.TargetOffset, this.m_structval);
		}

		internal override void StorePassableNumericValTo(NumericValue nv, ReferenceValue rf)
		{
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			if(rf.TargetOffset.IsEmptyAccessPath)
			{
				Debug.Assert(this.LocationType is IRPrimitiveNumericType || this.LocationType is IREnumType);
				Debug.Assert((this.LocationType is IRPrimitiveNumericType && this.LocationType == nv.ValType) || (this.LocationType is IREnumType && ((IREnumType)this.LocationType).UnerlyingNumericType == nv.ValType));

				TracingSystem.LogOldNumeric(this.Address, IRBasicAccessPath.EmptyAccessPath, this.m_numval);

				this.m_numval = nv;
			}
			else
			{
				TracingSystem.LogOldNumeric(this.Address, rf.TargetOffset, CompositeValue.ReadPassableNumericFrom(rf.TargetOffset, this.m_structval));

				CompositeValue.StorePassableNumericValTo(nv, rf.TargetOffset, this.m_structval);
			}
		}

		internal override CompositeValueEntry[] ReadCompositeFrom(ReferenceValue rf)
		{
			TracingSystem.LogUseFromLocation(this.Address, rf.TargetOffset, this.LocationType);

			return CompositeValue.ReadCompositeFrom(rf.TargetOffset, this.m_structval, this.LocationType);
		}

		internal override void StoreCompositeTo(CompositeValueEntry[] cv, ReferenceValue rf)
		{
			TracingSystem.LogOldComposite(this.Address, rf.TargetOffset, CompositeValue.ReadCompositeFrom(rf.TargetOffset, this.m_structval, this.LocationType));
			TracingSystem.LogModToLocation(this.Address, rf.TargetOffset, this.LocationType);

			CompositeValue.StoreCompositeTo(cv, rf.TargetOffset, this.m_structval, this.LocationType);
		}
	}
}
